\subsection{Еще одна оптимизация циклов}

Если вы обрабатываете все элементы некоторого массива, который находится в глобальной памяти, компилятор может оптимизировать
это.
Например, вычисляем сумму всех элементов массива из 128-и \IT{int}-ов:

\begin{lstlisting}[style=customc]
#include <stdio.h>

int a[128];

int sum_of_a()
{
	int rt=0;
	
	for (int i=0; i<128; i++)
		rt=rt+a[i];

	return rt;
};

int main()
{
	// инициализация
	for (int i=0; i<128; i++)
		a[i]=i;
	
	// вычисляем сумму
	printf ("%d\n", sum_of_a());
};
\end{lstlisting}

Оптимизирующий GCC 5.3.1 (x86) может сделать так (\IDA):

\begin{lstlisting}[style=customasmx86]
.text:080484B0 sum_of_a        proc near
.text:080484B0                 mov     edx, offset a
.text:080484B5                 xor     eax, eax
.text:080484B7                 mov     esi, esi
.text:080484B9                 lea     edi, [edi+0]
.text:080484C0
.text:080484C0 loc_80484C0:            ; CODE XREF: sum_of_a+1B
.text:080484C0                 add     eax, [edx]
.text:080484C2                 add     edx, 4
.text:080484C5                 cmp     edx, offset __libc_start_main@@GLIBC_2_0
.text:080484CB                 jnz     short loc_80484C0
.text:080484CD                 rep retn
.text:080484CD sum_of_a        endp
.text:080484CD

...

.bss:0804A040                 public a
.bss:0804A040 a               dd 80h dup(?) ; DATA XREF: main:loc_8048338
.bss:0804A040                               ; main+19
.bss:0804A040 _bss            ends
.bss:0804A040
extern:0804A240 ; ===========================================================================
extern:0804A240
extern:0804A240 ; Segment type: Externs
extern:0804A240 ; extern
extern:0804A240                 extrn __libc_start_main@@GLIBC_2_0:near
extern:0804A240                            ; DATA XREF: main+25
extern:0804A240                            ; main+5D
extern:0804A244                 extrn __printf_chk@@GLIBC_2_3_4:near
extern:0804A248                 extrn __libc_start_main:near
extern:0804A248                            ; CODE XREF: ___libc_start_main
extern:0804A248                            ; DATA XREF: .got.plt:off_804A00C
\end{lstlisting}

И что же такое \TT{\_\_libc\_start\_main@@GLIBC\_2\_0} на \TT{0x080484C5}?
Это метка, находящаяся сразу за концом массива \TT{a[]}.
Эта ф-ция может быть переписана так:

\begin{lstlisting}[style=customc]
int sum_of_a_v2()
{
	int *tmp=a;
	int rt=0;
	
	do
	{
		rt=rt+(*tmp);
		tmp++;
	}
	while (tmp<(a+128));

	return rt;
};
\end{lstlisting}

Первая версия имеет счетчик \IT{i}, и адрес каждого элемента массива вычисляется на каждой итерации.
Вторая версия более оптимизирована: указатель на каждый элемент массива всегда готов, и продвигается на 4 байта вперед
на каждой итерации.
Как проверить, закончился ли цикл?
Просто сравните указатель с адресом сразу за концом массива, который, как случилось в нашем случае, это просто адрес
импортируемой из Glibc 2.0 ф-ции \TT{\_\_libc\_start\_main()}.
Такой когд иногда сбивает с толку, и это очень популярный оптимизационный трюк, поэтому я сделал этот пример.

Моя вторая версия очень близка к тому, что сделал GCC, и когда я компилирую её, код почти такой как и в первой версии,
но две первых инструкции поменены местами:

\begin{lstlisting}[style=customasmx86]
.text:080484D0                 public sum_of_a_v2
.text:080484D0 sum_of_a_v2     proc near
.text:080484D0                 xor     eax, eax
.text:080484D2                 mov     edx, offset a
.text:080484D7                 mov     esi, esi
.text:080484D9                 lea     edi, [edi+0]
.text:080484E0
.text:080484E0 loc_80484E0:            ; CODE XREF: sum_of_a_v2+1B
.text:080484E0                 add     eax, [edx]
.text:080484E2                 add     edx, 4
.text:080484E5                 cmp     edx, offset __libc_start_main@@GLIBC_2_0
.text:080484EB                 jnz     short loc_80484E0
.text:080484ED                 rep retn
.text:080484ED sum_of_a_v2     endp
\end{lstlisting}

Надо сказать, эта оптимизация возможна если компилятор, во время компиляции, может расчитать адрес за концом массива.
Это случается если массив глобальный и его размер фиксирован.

Хотя, если адрес массива не известен во время компиляции, но его размер фиксирован, адрес метки за концом массива
можно вычислить в начале цикла.
% FIXME <!-- \ref{} to example -->

